//--------------------------
//---RockAttack Minigame----
//-----Made By Brian Nevec--
//--------------------------

GAME.Name = "Rock Attack";
GAME.Author = "Brian Nevec";
GAME.Version = "version 1.0";
GAME.Selectable = true;

GAME.Size = tgm.ScreenSize;
GAME.Center = GAME.Size / 2;

GAME.Update = true;

GAME.Rocks = {};
GAME.Blasts = {};
GAME.Explosions = {};

GAME.ShipAngle = 180;
GAME.Lives = 0;
GAME.MaxLives = 3;
GAME.ShipSize = 15;
GAME.ShipSpeed = 0.025;
GAME.BlastSize = 6;

GAME.Score = 0;
GAME.ScoreToLife = 250;
GAME.LastScore = 0;
GAME.HighScore = 0;
GAME.ShouldSaveScore = true;

GAME.NextFire = 0;
GAME.FireDelay = 0.2;
GAME.FireSpread = 0.03;
GAME.AddFireSpread = true;
GAME.BlastSpeed = 5.2; //10
GAME.DoMegaBlast = true;
GAME.MegaBlastChance = 15;

GAME.RockSpawnDelay = 0.6;
GAME.RockSize = 12;
GAME.RockSpeed = 4;

GAME.SpeedUpdate = 0.1;
GAME.NextUpdate = 0;
GAME.NextRockSpawn = 0;

GAME.DrawStats = false;

GAME.DrawStars = true;
GAME.Stars = {};
GAME.StarCount = 150;

GAME.UseDifficulty = true;
GAME.SpeedIncrease = 0.8;
GAME.SpawnIncrease = -0.1;

GAME.DefaultSpeed = GAME.RockSpeed;
GAME.DefaultSpawn = GAME.RockSpawnDelay;

GAME.Sounds =
{
	GameOver = "ambient/levels/citadel/pod_open1.wav",
	RockHit = "physics/concrete/concrete_break3.wav",
	ShipHit = "weapons/stunstick/stunstick_impact2.wav",
	Fire = "weapons/ar2/fire1.wav",
	MegaFire = "weapons/mortar/mortar_fire1.wav",
	ExtraLife = "ambient/levels/citadel/pod_close1.wav"
}

for I,Name in pairs(GAME.Sounds) do

	GAME.Sounds[I] = Sound(Name);

end

function GAME:Initialize()

	math.randomseed(CurTime());
	
	self:ResetGame();
	
	self:LoadScore();
	
	if self.DrawStars then

		for I = 1,self.StarCount do

			local Pos = self:AddStar();
			
			table.insert(self.Stars,Pos);

		end

	end
	
	self:DisableMouse();

end

function GAME:AddStar()

	local Pos = {};
	Pos.x = math.Rand(0,self.Size);
	Pos.y = math.Rand(0,self.Size);

	if self.DrawStats then

		while Pos.x >= 132 && Pos.y >= 180 do

			Pos = self:AddStar();

		end

	end

	return Pos;

end

function GAME:ResetGame()

	if self.Score > self.HighScore then

		self.HighScore = self.Score;

		self:SaveScore();

	end

	self.Score = 0;
	self.LastScore = 0;
	self.Lives = self.MaxLives;

	self.NextFire = 0;
	self.NextUpdate = 0;
	self.NextRockSpawn = 0;
	
	for I = 1,#self.Rocks do

		table.remove(self.Rocks,I);

	end
	
	for I = 1,#self.Blasts do

		table.remove(self.Blasts,I);

	end

	self.ShipAngle = math.Rand(0,360);

	self.RockSpeed = self.DefaultSpeed;
	self.RockSpawnDelay = self.DefaultSpawn;

end

function GAME:PlaySound(Name)

	LocalPlayer():EmitSound(Name,50)

end

function GAME:SaveScore()

	if self.ShouldSaveScore then

		file.Write("ra_highscore.txt",self.HighScore);

	end

end

function GAME:LoadScore()

	if file.Exists("tgm_RockAttack/highscore.txt") then

		file.Write("ra_highscore.txt",file.Read("tgm_RockAttack/highscore.txt"));

		file.Delete("tgm_RockAttack/highscore.txt");

	end

	self.HighScore = tonumber(file.Read("ra_highscore.txt") or 0);

end

function GAME:Think()

	if self.Update then

		if self:BindDown("+moveleft") then

			self.ShipAngle = self.ShipAngle + self.ShipSpeed;

		elseif self:BindDown("+moveright") then

			self.ShipAngle = self.ShipAngle - self.ShipSpeed;

		end
		
		if CurTime() > self.NextUpdate then

			for I,Blast in pairs(self.Blasts) do

				self.Blasts[I].Distance = Blast.Distance + Blast.Speed;

			end
			
			for I,Rock in pairs(self.Rocks) do

				self.Rocks[I].Distance = Rock.Distance + Rock.Speed;

			end
			
			if CurTime() > self.NextRockSpawn then

				self:CreateRock();
				
				self.NextRockSpawn = CurTime() + self.RockSpawnDelay;

			end
						
			self.NextUpdate = CurTime() + self.SpeedUpdate;

		end
		
		//Collisions and Garbage removal
		//We gotta do this as fast as possible, so put it outside the delay if
		for BlastIndex,Blast in pairs(self.Blasts) do

			for RockIndex,Rock in pairs(self.Rocks) do

				if self:Colliding(RockIndex,BlastIndex) then

					local Pos = {};
					Pos.x = Rock.StartPos.x + math.sin(Rock.Direction) * Rock.Distance;
					Pos.y = Rock.StartPos.y + math.cos(Rock.Direction) * Rock.Distance;
					
					table.remove(self.Rocks,RockIndex);
					
					if !Blast.Mega || Blast.Last then

						table.remove(self.Blasts,BlastIndex);

					else

						Blast.Last = true;

					end
					
					self:CreateExplosion(Pos);

					self.Score = self.Score + 10;
					
					if self.Score - self.LastScore >= self.ScoreToLife then

						self.Lives = self.Lives + 1;
						
						self.LastScore = self.Score;
						
						self:PlaySound(self.Sounds.ExtraLife);
						
						if self.UseDifficulty then

							self.RockSpeed = self.RockSpeed + self.SpeedIncrease;
							self.RockSpawnDelay = self.RockSpawnDelay + self.SpawnIncrease;

						end

					end
					
					self:PlaySound(self.Sounds.RockHit);

				elseif self:CollidingWithShip(RockIndex) then

					local Pos = {};
					Pos.x = Rock.StartPos.x + math.sin(Rock.Direction) * Rock.Distance;
					Pos.y = Rock.StartPos.y + math.cos(Rock.Direction) * Rock.Distance;
						
					table.remove(self.Rocks,RockIndex);
					
					self:CreateExplosion(Pos);

					self.Lives = self.Lives - 1;

					if self.Lives <= 0 then

						self:ResetGame();
						
						self:PlaySound(self.Sounds.GameOver);

					else

						self:PlaySound(self.Sounds.ShipHit);

					end

				else

					local RockX = Rock.StartPos.x + math.sin(Rock.Direction) * Rock.Distance;
					local RockY = Rock.StartPos.y + math.cos(Rock.Direction) * Rock.Distance;
					
					if RockX > self.Size || RockX < 0 || RockY > self.Size || RockY < 0 then

						table.remove(self.Rocks,RockIndex);

					end

				end

			end
			
			local X = self.Center + math.sin(Blast.Direction) * (Blast.Distance + 20);
			local Y = self.Center + math.cos(Blast.Direction) * (Blast.Distance + 20);
			
			if X > self.Size || X < 0 || Y > self.Size || Y < 0 then

				table.remove(self.Blasts,BlastIndex);

			end

		end

	end

end

function GAME:RandomPos()

	local Pos = {};
	Pos.x = math.Rand(0,self.Size);
	Pos.y = math.Rand(0,self.Size);
	
	local Side = math.random(0,4);
	
	if Side == 1 then

		Pos.x = 0;

	elseif Side == 2 then

		Pos.y = 0;

	elseif Side == 3 then

		Pos.x = self.Size;

	else

		Pos.y = self.Size;

	end
	
	return Pos;

end

//will fix this later
function GAME:RockAngle(Pos)

	local Center = Vector(self.Center,self.Center,0);
	local Target = Vector(Pos.x,Pos.y,0);
	local Offset = (Center - Target);
	
	return Offset:Angle().y;

end

function GAME:CreateRock()

	local Rock = {};
	Rock.Speed = self.RockSpeed;
	Rock.StartPos = self:RandomPos();
	Rock.Direction = self:RockAngle(Rock.StartPos);
	Rock.Distance = 0;
	
	table.insert(self.Rocks,Rock);

end

function GAME:CreateExplosion(Pos)

	local Expl = {};
	Expl.Pos = Pos;
	Expl.StartTime = CurTime();
	Expl.Length = 2;
	
	table.insert(self.Explosions,Expl);

end

function GAME:DrawExplosions()

	for I,Expl in pairs(self.Explosions) do

		if CurTime() < Expl.StartTime + Expl.Length then

			surface.SetDrawColor(255,0,0,255);
			
			surface.DrawRect(Expl.Pos.x - 10,Expl.Pos.y - 5,20,10);
			surface.DrawRect(Expl.Pos.x - 5,Expl.Pos.y - 10,10,20);
			
			surface.SetDrawColor(255,255,0,255);
			
			surface.DrawRect(Expl.Pos.x - 5,Expl.Pos.y - 5,10,10);

		else

			table.remove(self.Explosions,I);

		end

	end

end

function GAME:Fire()

	local Blast = {};
	Blast.Direction = self.ShipAngle;
	Blast.Distance = 0;
	Blast.Speed = self.BlastSpeed;
	Blast.Last = true;
	Blast.Mega = false;

	if self.AddFireSpread then

		Blast.Direction = Blast.Direction + math.sin(CurTime() * 15) * self.FireSpread;

	end
	
	if self.DoMegaBlast then

		if math.Rand(1,self.MegaBlastChance) < 2 then

			Blast.Last = false;
			Blast.Mega = true;

		end

	end
	
	table.insert(self.Blasts,Blast);
	
	if !Blast.Mega then

		self:PlaySound(self.Sounds.Fire);

	else

		self:PlaySound(self.Sounds.MegaFire);

	end

end

function GAME:Colliding(Rock,Blast)

	Rock = self.Rocks[Rock];
	Blast = self.Blasts[Blast];

	if Rock && Blast then

		local RockX = Rock.StartPos.x + math.sin(Rock.Direction) * Rock.Distance;
		local RockY = Rock.StartPos.y + math.cos(Rock.Direction) * Rock.Distance;
	
		local RockMaxX = RockX + self.RockSize;
		local RockMaxY = RockY + self.RockSize;
	
		local BlastX = self.Center + math.sin(Blast.Direction) * (Blast.Distance + 20);
		local BlastY = self.Center + math.cos(Blast.Direction) * (Blast.Distance + 20);

		local BlastMaxX = BlastX + self.BlastSize;
		local BlastMaxY = BlastY + self.BlastSize;
		
		local Corner2X = BlastX + self.BlastSize;
		local Corner2Y = BlastY;
		
		local Corner3X = BlastX;
		local Corner3Y = BlastY + self.BlastSize;
		
		local Corn1Coll = ((BlastX >= RockX && BlastX <= RockMaxX) && (BlastY >= RockY && BlastY <= RockMaxY));
		local Corn2Coll = ((Corner2X >= RockX && Corner2X <= RockMaxX) && (Corner2Y >= RockY && Corner2Y <= RockMaxY));
		local Corn3Coll = ((Corner3X >= RockX && Corner3X <= RockMaxX) && (Corner3Y >= RockY && Corner3Y <= RockMaxY));
		local Corn4Coll = ((BlastMaxX >= RockX && BlastMaxX <= RockMaxX) && (BlastMaxY >= RockY && BlastMaxY <= RockMaxY));
		
		return Corn1Coll || Corn2Coll || Corn3Coll || Corn4Coll;

	end
	
	return false;

end


function GAME:CollidingWithShip(Rock)

	Rock = self.Rocks[Rock];
	
	local RockX = Rock.StartPos.x + math.sin(Rock.Direction) * Rock.Distance + self.RockSize / 2;
	local RockY = Rock.StartPos.y + math.cos(Rock.Direction) * Rock.Distance + self.RockSize / 2;

	return (RockX > self.Center - self.ShipSize && RockX < self.Center + self.ShipSize) && (RockY > self.Center - self.ShipSize && RockY < self.Center + self.ShipSize);

end

function GAME:PrepNumber(Num)

	Num = tostring(Num);

	while #Num < 3 do

		Num = "0"..Num;

	end

	return Num;

end

function GAME:Render()

	local Center = self.Center;
	
	if self.DrawStars then
		
		for _,Pos in pairs(self.Stars) do

			Alpha = 150 + (math.sin((CurTime() + math.Rand(5,15)) * 25) * 105);

			surface.SetDrawColor(255,255,255,Alpha);

			surface.DrawRect(Pos.x,Pos.y,2,2);

		end

	end

	local C = self.Lives * 50;
	C = math.Clamp(C,0,255);

	surface.SetDrawColor(C,C,C,255);
	
	local Top,Left,Right = {},{},{};
	
	//i got these kinda wrong, but they works
	Top.x = math.sin(self.ShipAngle) * self.ShipSize;
	Top.y = math.cos(self.ShipAngle) * self.ShipSize;
	
	Left.x = math.sin(self.ShipAngle - 120) * self.ShipSize * -1;
	Left.y = math.cos(self.ShipAngle - 120) * self.ShipSize * -1;
	
	Right.x = math.sin(self.ShipAngle + 120) * self.ShipSize * -1;
	Right.y = math.cos(self.ShipAngle + 120) * self.ShipSize * -1;

	//could simplify this a bit
	local Data =
	{
		{
			x = Top.x + Center,
			y = Top.y + Center,
			u = 0,
			v = 0
		},
		{
			x = Left.x + Center,
			y = Left.y + Center,
			u = 0,
			v = 0
		},
		{
			x = Right.x + Center,
			y = Right.y + Center,
			u = 0,
			v = 0
		}
	}
	
	surface.DrawPoly(Data);
	
	for _,Blast in pairs(self.Blasts) do

		local X = self.Center + math.sin(Blast.Direction) * (Blast.Distance + 20);
		local Y = self.Center + math.cos(Blast.Direction) * (Blast.Distance + 20);
		
		surface.SetDrawColor(179,255,255,255);
		
		if Blast.Mega then

			surface.SetDrawColor(0,0,255,255);

		end
		
		surface.DrawRect(X - self.BlastSize / 2,Y - self.BlastSize / 2,self.BlastSize,self.BlastSize);

	end
	
	for _,Rock in pairs(self.Rocks) do

		local X = Rock.StartPos.x + math.sin(Rock.Direction) * Rock.Distance;
		local Y = Rock.StartPos.y + math.cos(Rock.Direction) * Rock.Distance;
		
		surface.SetDrawColor(152,97,97,255);
		
		surface.DrawRect(X - self.RockSize / 2,Y - self.RockSize / 2,self.RockSize,self.RockSize);

	end
	
	self:DrawExplosions();

	local Col = Color(255,255,255,200);

	local TextX = self.Center + math.sin(self.ShipAngle + 180) * (self.ShipSize * 2);
	local TextY = self.Center + math.cos(self.ShipAngle + 180) * (self.ShipSize * 2);

	draw.SimpleText(self.Score.." p","default",TextX,TextY,Col,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER);
	
	local TextX = self.Center + math.sin(self.ShipAngle - 180) * (self.ShipSize * 2);
	local TextY = self.Center + math.cos(self.ShipAngle - 180) * (self.ShipSize * 2);

	draw.SimpleText((self.Lives - 1).." l","default",TextX,TextY,Col,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER);
	
	if self.ShouldSaveScore then

		draw.SimpleText(self.HighScore.." tp","default",self.Center,self.Center / 4,Col,TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER);

	end
	
	if self.DrawStats then

		local Num = self:PrepNumber(math.Clamp(self.MaxLives - self.Lives,0,500));
		local W1,H1 = surface.GetTextSize("Hits taken: "..Num);
		draw.SimpleText("Hits taken: "..Num,"default",self.Size - 10,self.Size - 20,Col,TEXT_ALIGN_RIGHT,TEXT_ALIGN_BOTTOM);

		local Num = self:PrepNumber(self.Score / 10);
		local W2,H2 = surface.GetTextSize("Rocks destroyed: "..Num);
		draw.SimpleText("Rocks destroyed: "..Num,"default",self.Size - 10,self.Size - 20 - 15,Col,TEXT_ALIGN_RIGHT,TEXT_ALIGN_BOTTOM);

		local Num = self:PrepNumber(#self.Blasts);
		local W3,H3 = surface.GetTextSize("Tracking blasts: "..Num);
		draw.SimpleText("Tracking blasts: "..Num,"default",self.Size - 10,self.Size - 20 - 15 - 15,Col,TEXT_ALIGN_RIGHT,TEXT_ALIGN_BOTTOM);

		local Num = self:PrepNumber(#self.Rocks);
		local W4,H4 = surface.GetTextSize("Tracking rocks: "..Num);
		draw.SimpleText("Tracking rocks: "..Num,"default",self.Size - 10,self.Size - 20 - 15 - 15 - 15,Col,TEXT_ALIGN_RIGHT,TEXT_ALIGN_BOTTOM);

		local X = self.Size - 5 - 5 - math.max(W1,W2,W3,W4) - 5;
		local Y = self.Size - 5 - H4 - 5 - H3 - 5 - H2 - 5 - H1;

		surface.SetDrawColor(Col.r,Col.g,Col.b,Col.a);

		surface.DrawLine(X,Y,X,self.Size);
		surface.DrawLine(X,Y,self.Size,Y);

	end

end

function GAME:BindPressed(Bind)

	//from base -- needs BaseClass
	self.Bind[Bind] = true;

	if self.Update then

		if Bind == "+jump" then

			if CurTime() > self.NextFire then

				self:Fire();
				
				self.NextFire = CurTime() + self.FireDelay;

			end

		end

	end

end

function GAME:Pause()

	self.Update = false;
	
	self:SaveScore();

end

function GAME:Unpause()

	self.Update = true;

end